package com.example.serverdemo.netty.device_tcp_server.function_module;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ArrayUtil;
import com.example.serverdemo.bean.HardwareParameterBean;
import com.example.serverdemo.common.Constant;
import com.example.serverdemo.common.DeviceCommunitionCommandReceive;
import com.example.serverdemo.netty.device_tcp_server.handler.DeviceHandler;
import com.example.serverdemo.netty.device_tcp_server.type.FunctionType;
import com.example.serverdemo.utils.crcutls.CRCUtil;
import com.example.serverdemo.utils.fileutils.FirmwareFileUtils;
import com.example.serverdemo.utils.log.PrintLogUtil;
import java.io.*;
import java.util.ArrayList;
import java.util.List;

public class CoreControllerFunctionModule extends BaseFunctionModule{
    private static final String ORDER_REMOTE_UPDATE = "UpDate";
    private static final String ORDER_READ_HARDWARE_PARAMETER = "GetParam";
    private static final String ORDER_CONFIGURATION_HARDWARE_PARAMETER = "PutParam";
    private static final int  PACKET_SIZE=1024 * 2;
    private FunctionType functionType;
    private RandomAccessFile remoteUpdateFirmwareRandomAccessFile;
    private int packetCount;
    private int packetIndex;

    public CoreControllerFunctionModule(DeviceHandler handler) {
        this.init();
        this.initRemoteUpdateFirmwareParam();
        this.bindDeviceHandler(handler);
    }
    @Override
    protected void init() {
        super.init();
    }
    @Override
    protected boolean success(Object obj) {
        try {
            switch (this.functionType){
                case FirmwareUpdate:
                    this.remoteUpdateFirmwareSuccess(obj);
                    break;
                case HardwareParameterReadBack:
                    this.readBackHardwareParameterSuccess(obj);
                    break;
                case HardwareParameterConfiguration:
                    this.configurationHardwareParameterSuccess(obj);
                    break;
            }
            return true;
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
        return false;
    }
    @Override
    protected boolean failed(String msg) {
        try {
            switch (this.functionType){
                case FirmwareUpdate:
                    this.remoteUpdateFirmwareFailed("Remote updateFirmware Failed :" + msg);
                    break;
                case HardwareParameterReadBack:
                    this.readBackHardwareParameterFailed("Hardware Parameter Read Back Failed :" + msg);
                    break;
                case HardwareParameterConfiguration:
                    this.configurationHardwareParameterFailed("Hardware Parameter Configuration Failed :" + msg);
                    break;
            }
            return true;
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
        return false;
    }
    private void initParams(){
        this.functionType = FunctionType.Null;
        this.handler.setFunctionKey(true);
        this.handler.currentFunctionType = FunctionType.Null;
    }
    @Override
    public boolean msgDistributionTransactionManagement(byte[] msg) {
        try {
            switch (this.functionType){
                case FirmwareUpdate:
                    this.remoteUpdateFirmwareMsgManagement(msg);
                    break;
                case HardwareParameterReadBack:
                    this.readBackHardwareParameterMsgManagement(msg);
                    break;
                case HardwareParameterConfiguration:
                    this.configurationHardwareParameterMsgManagement(msg);
                    break;
                case Null:
                    break;
            }
            return true;
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
        return false;
    }
    private byte getOrderMark(){
        switch (this.functionType){
            case FirmwareUpdate:
            case HardwareParameterConfiguration:
                return 0x05;
            case HardwareParameterReadBack:
                return 0x01;
            case Null:
            default:
                return 0x00;
        }
    }
    private byte getDataMark(){
        if (this.packetIndex == (this.packetCount - 1)){
            return 0x02;
        }else {
            return 0x06;
        }
    }
    //restart
    public boolean restart(){
        try {
            this.stopOverTimeDetectionTask();
            this.sendOrder("Reset",null,Convert.intToByte(0x01),Convert.intToByte(this.handler.currentDeviceInfo.getDeviceAddress()),false);
            return true;
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
        return false;
    }
    //Remote Update Firmware Function Module
    private void initRemoteUpdateFirmwareParam(){
        try {
            this.packetIndex = 0;
            this.packetCount = 0;
            if (this.remoteUpdateFirmwareRandomAccessFile != null){
                this.remoteUpdateFirmwareRandomAccessFile.close();
                this.remoteUpdateFirmwareRandomAccessFile = null;
            }
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
    }
    public boolean remoteUpdateFirmware(String sourceFilePath){
        try {
            if (!this.handler.isFunctionKey()) return false;
            if (this.handler.deviceId == null) return false;
//            if (!FirmwareFileUtils.getInstance().duplicateFirmwareFile(this.handler.deviceId,sourceFilePath)) return false;
            this.handler.setFunctionKey(false);
            this.functionType = FunctionType.FirmwareUpdate;
            this.packetIndex = 0;
            this.remoteUpdateFirmwareRandomAccessFile = new RandomAccessFile(sourceFilePath,"r");
//            this.remoteUpdateFirmwareRandomAccessFile = new RandomAccessFile("C://Server/CopyFile/" + this.handler.deviceId + ".xbin","r");
            byte[] buff = new byte[(int) this.remoteUpdateFirmwareRandomAccessFile.length()];
            this.packetCount = (buff.length / PACKET_SIZE) + (buff.length % PACKET_SIZE== 0 ? 0 : 1);
            if (packetCount <= 0){
                this.remoteUpdateFirmwareFailed("固件异常，无数据");
                return false;
            }
            this.remoteUpdateFirmwareRandomAccessFile.read(buff);
            int fileCRC = CRCUtil.getInstance().calcCrc16(buff);
            List<String> params = new ArrayList<>();
            params.add("Update.xbin" );
            params.add("" + buff.length);
            params.add("" + fileCRC);
            this.sendOrder(ORDER_REMOTE_UPDATE,params,this.getOrderMark(), Convert.intToByte(this.handler.currentDeviceInfo.getDeviceAddress()),true);
            return true;
        }catch (Exception e){
            this.functionType = FunctionType.Null;
            PrintLogUtil.printError(this,e);
        }
        return false;
    }
    private boolean remoteUpdateFirmwareTask(){
        try {
            if (this.remoteUpdateFirmwareRandomAccessFile != null){
                byte[] buff = new byte[PACKET_SIZE];
                this.remoteUpdateFirmwareRandomAccessFile.seek(this.packetIndex * PACKET_SIZE);
                int len = this.remoteUpdateFirmwareRandomAccessFile.read(buff);
                if (len > 0){
                    this.sendData(ArrayUtil.sub(buff,0,len),this.getDataMark(),Convert.intToByte(this.handler.currentDeviceInfo.getDeviceAddress()),true);
                    this.packetIndex++;
                }
            }
            return true;
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
        return false;
    }
    private void updateRemoteUpdateFirmwareTaskProgress(){
        if (this.packetCount > 0){
            int progress = (int)(this.packetIndex * 100.0) / this.packetCount;
            this.handler.redisUtils.set(Constant.PROGRESS + this.handler.deviceId,String.valueOf(progress));
        }
    }
    private void remoteUpdateFirmwareMsgManagement(byte[] msg){
        byte[] data = ArrayUtil.sub(msg,8,msg.length);
        String msgStr = new String(data);
        this.stopOverTimeDetectionTask();
        switch (msgStr){
            case DeviceCommunitionCommandReceive.OTAReceive.OK_DECODE:
            case DeviceCommunitionCommandReceive.OTAReceive.SENDDATA:
                this.updateRemoteUpdateFirmwareTaskProgress();
                this.remoteUpdateFirmwareTask();
                break;
            case DeviceCommunitionCommandReceive.OTAReceive.COMPLETED:
                this.updateRemoteUpdateFirmwareTaskProgress();
                this.success(null);
                break;
            default:
                this.failed("remoteUpdateFirmware receive Error Message：" + msgStr);
                break;
        }
    }
    private void remoteUpdateFirmwareSuccess(Object obj){
        try {
            PrintLogUtil.printInfo(this,"RemoteUpdate " + this.handler.deviceId + " Success");
            this.initRemoteUpdateFirmwareParam();
            this.initParams();
            this.handler.closeHandler();
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
    }
    private void remoteUpdateFirmwareFailed(String msg){
        try {
            PrintLogUtil.printInfo(this,"Remote Update " + this.handler.deviceId + " Failed : " + msg);
            this.initRemoteUpdateFirmwareParam();
            this.initParams();
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
    }
    //Hardware Parameter Read Back
    public boolean readBackHardwareParameter(){
        try {
            if (!this.handler.isFunctionKey()) return false;
            if (this.handler.deviceId == null) return false;
            this.handler.setFunctionKey(false);
            this.functionType = FunctionType.HardwareParameterReadBack;
            this.sendOrder(ORDER_READ_HARDWARE_PARAMETER,null,this.getOrderMark(),Convert.intToByte(this.handler.currentDeviceInfo.getDeviceAddress()),true);
            return true;
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
        return false;
    }
    private void readBackHardwareParameterMsgManagement(byte[] msg){
        this.stopOverTimeDetectionTask();
        byte[] data = ArrayUtil.sub(msg,8,msg.length);
        HardwareParameterBean bean = HardwareParameterBean.build(data);
        if (bean != null){
            bean.setDeviceId(this.handler.deviceId);
            this.success(bean);
        }else {
            this.failed("Receive Hardware Parameter Data Error");
        }
    }
    private void readBackHardwareParameterSuccess(Object obj){
        PrintLogUtil.printInfo(this,"Read Hardware Parameter Success");
        this.handler.hardwareParameterService.addHardwareParameter((HardwareParameterBean)obj);
        this.initParams();
        this.handler.readBackLightControllerParameter();
    }
    private void readBackHardwareParameterFailed(String msg){
        PrintLogUtil.printInfo(this,"Read Back Hardware Parameter Failed :" + msg);
        this.initParams();
    }
    //Hardware Parameter Configuration
    private void initConfigurationHardwareParameter(){
        this.packetCount = 0;
        this.packetIndex = 0;
    }
    public boolean configurationHardwareParameter(){
        try {
            if (!this.handler.isFunctionKey()) return false;
            if (this.handler.deviceId == null) return false;
            this.handler.setFunctionKey(false);
            this.functionType = FunctionType.HardwareParameterConfiguration;
            HardwareParameterBean bean = this.handler.hardwareParameterService.queryHardwareParameter(this.handler.deviceId);
            if (bean == null) return false;
            List<String> params = new ArrayList<>();
            params.add("Hardware.bin");
            byte[] data = bean.toHexData();
            params.add(String.valueOf(data.length));
            params.add(String.valueOf(CRCUtil.getInstance().calcCrc16(data)));
            this.packetCount = 1;
            this.packetIndex = 0;
            this.sendOrder(ORDER_CONFIGURATION_HARDWARE_PARAMETER,params,this.getOrderMark(),Convert.intToByte(this.handler.currentDeviceInfo.getDeviceAddress()),true);
            return true;
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
       return false;
    }
    private boolean configurationHardwareParameterTask(){
        try {
            HardwareParameterBean bean = this.handler.hardwareParameterService.queryHardwareParameter(this.handler.deviceId);
            if (bean == null) return false;
            bean.toHexData();
            this.sendData(bean.toHexData(),this.getDataMark(),Convert.intToByte(this.handler.currentDeviceInfo.getDeviceAddress()),true);
            return true;
        }catch (Exception e){
            PrintLogUtil.printError(this,e);
        }
        return false;
    }
    private void configurationHardwareParameterMsgManagement(byte[] msg){
        this.stopOverTimeDetectionTask();
        byte[] data = ArrayUtil.sub(msg,8,msg.length);
        String msgStr = new String(data);
        switch (msgStr){
            case DeviceCommunitionCommandReceive.ConfigurationHardwareParameter.OK_DECODE:
                this.configurationHardwareParameterTask();
                break;
            case DeviceCommunitionCommandReceive.ConfigurationHardwareParameter.COMPLETED:
                this.success(null);
                break;
        }
    }
    private void configurationHardwareParameterSuccess(Object obj){
        PrintLogUtil.printInfo(this,"Configuration Hardware Parameter Success");
        this.initConfigurationHardwareParameter();
        this.initParams();
        this.handler.closeHandler();
    }
    private void configurationHardwareParameterFailed(String msg){
        PrintLogUtil.printInfo(this,"Configuration Hardware Parameter Failed : " + msg);
        this.initConfigurationHardwareParameter();
        this.initParams();
    }
}
